/**
 * NormalDistribution : Definition de la loi Normale
 *
 * @author maria ladoue
 * @version 0.1, 30/08/02 
 */
package br.unb.statistic.distribution;

import java.util.HashMap;
import br.unb.utils.*;
import VisualNumerics.math.Statistics;
import br.unb.statistic.InterPacketTimeGenerator;
import br.unb.statistic.RandomGenerator;
import br.unb.statistic.inference.Inference;

public class NormalDistribution extends GenericContinuousDistribution implements RandomGenerator, InterPacketTimeGenerator {

    /**    
     * Equation de la densite:<BR>
     *  f(x) = exp(-((x-mu) / (sqrt(2)*sigma) )^2)/(sqrt(2*pi)*sigma)<BR>
     *      where mu = mean of the normal distribution <BR>
     *            sigma = standard deviation must be<BR>
     */
    private static int MU = 0;
    private static int SIGMA = 1;

    public NormalDistribution() {
        paramsCount = 2;
        name = new String("Normal");
        paramNames = new String[paramsCount];
        paramNames[MU] = new String("mu");
        paramNames[SIGMA] = new String("sigma");
        params = new double[paramsCount];
        params[MU] = 0.0d;
        params[SIGMA] = 1.0d;
    }

    public NormalDistribution(double mu, double sigma)
            throws IllegalArgumentException {
        this();

        if (sigma > 0) {
            params[MU] = mu;
            params[SIGMA] = sigma;
        } else {
            throw new IllegalArgumentException(
                    Resource.getString("POISSONDISTRIBUTION.SETPARAMS_SIGMA_<_0")
                    + params[SIGMA]);
        }
    }

    public NormalDistribution(double[] params)
            throws IllegalArgumentException {
        this();
        if (hasValidParam(params)) {
            setParams(params);
        } else {
            throw new IllegalArgumentException(Resource.getString("NORMALDISTRIBUTION.CONSTRUCTOR_SIGMA_<_0") + params[SIGMA]);
        }
    }

    public boolean hasValidParam(double[] desParams) {
        if (desParams[SIGMA] > 0) {
            return true;
        } else {
            return false;
        }
    }

    public void setParams(double[] params)
            throws IllegalArgumentException {

        if (params.length >= paramsCount && params[SIGMA] > 0) {
            this.params[MU] = params[MU];
            this.params[SIGMA] = params[SIGMA];
        } else {
            throw new IllegalArgumentException(Resource.getString("NORMALDISTRIBUTION.SETPARAMS_SIGMA_<_0") + params[SIGMA]);
        }
    }

    public void setParam(int idx, double p)
            throws IllegalArgumentException {

        if (idx < paramsCount && (idx != SIGMA || p > 0)) {
            params[idx] = p;
        } else {
            throw new IllegalArgumentException(Resource.getString("NORMALDISTRIBUTION.SETPARAM_SIGMA_AND_MU_MUST_BE_GREATER_THAN_0."));
        }
    }

    /**
     * Returns the value of the probability distribution function at x
     */
    public double pdf(double x) {

        return (1.0d / (params[SIGMA] * Math.sqrt(2.0d * Math.PI) * Math.exp(Math.pow((x - params[MU]) / params[SIGMA],
                2.0d) / 2.0d)));
    }

    /**
     * Returns the value of the cumulative distribution function at x
     */
    public double cdf(double x) {

        return normalStandardCdf((x - params[MU]) / params[SIGMA]);
    }

    /**
     * Return the value of Inverse Function at x
     * @param x with x>=0 and x<=1
     */
    public double inverseCdf(double x) {
        //return 0;
        //return params[MU] + params[SIGMA]*Mathematic.invNormal(x);
        return params[MU] + params[SIGMA] * Statistics.inverseNormalCdf(x);
    }

    public double mean() {

        return params[MU];
    }

    public double var() {

        return Math.pow(params[SIGMA], 2.0d);
    }

    public double rand() {

        return RandomGenerator.random.nextGaussian() * params[SIGMA] + params[MU];
    }

    public double nextInterPacketTime() {
        return rand();
    }

    public String getEquation() {
        return new String(Resource.getString("F(X)"));
    }

    public void setParameters(HashMap parameters) {
        double mean;
        if (parameters.get("mean") != null) {
            mean = Double.parseDouble((String) parameters.get("mean"));
        } else {
            mean = 1.0d / Double.parseDouble((String) parameters.get("rate"));
        }
        double variance = Double.parseDouble((String) parameters.get("variance"));
        if (variance > 0) {
            params[MU] = mean;
            params[SIGMA] = Math.sqrt(variance);
        }
    }

    public void estimateParameters(Inference inf) {
        double mean = inf.getMean();
        long n = inf.getSampleSize();
        double sigma = Math.sqrt(((double) (n - 1) / n) * inf.getVariance());
        params[MU] = mean;
        params[SIGMA] = sigma;
    }

    /*public static void main(String args[]){
    NormalDistribution dist = new NormalDistribution(0,1);
    System.out.println(dist.inverseCdf(0.6));
     */
    /*
    public static void main(String args[]) {
        NormalDistribution dist = new NormalDistribution(3.5, 5);
        Log log = new Log("normallemon", false);
        for (int i = 0; i < 10000; i++) {
            log.write("" + dist.rand());
        }
        log.close();
    }*/
}
